#include "tcpserver.h"

#include <algorithm>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <sys/stat.h>

#include "acceptor.h"
#include "address.h"
#include "connection.h"
#include "ioloop.h"
#include "process.h"
#include "signaler.h"
#include "sockutil.h"
#include "timer.h"
#include "timingwheel.h"

#include "servconf.h"

using namespace std;

namespace tnet
{
    const int defaultIdleTimeout = 120;

    void dummyRunCallback(IOLoop*)
    {
    }

    TcpServer::TcpServer():
    	m_loop(0),
		m_et(false),
		m_runCallback(std::bind(&dummyRunCallback, _1))
    {
        m_process = std::make_shared<Process>();
        m_running = false;
        m_maxIdleTimeout = defaultIdleTimeout;
		m_reuseport = false;
        m_isunsock = false;
        m_affinity = false;
    }

    TcpServer::~TcpServer()
    {
        if(m_loop)
        {
            delete m_loop;
        }
    }

    int TcpServer::listen(const Address& addr, const ConnEventCallback_t& callback)
    {
        LOG_INFO("listen %s:%d", addr.ipstr().c_str(), addr.port());

        NewConnCallback_t cb = std::bind(&TcpServer::onNewConnection, this, _1, _2, callback);
        AcceptorPtr_t acceptor = std::make_shared<Acceptor>(cb);

        if( (m_isunsock ? acceptor->listen(m_unpath) :  acceptor->listen(addr) ) < 0)
        {
            LOG_ERROR("listen ERROR:%s",errorMsg(errno));
            return -1;
        }

		m_acceptors.push_back(acceptor);
        return 0;
    }

	void TcpServer::start(size_t maxProcess)
    {
        if(maxProcess > 1)
        {
            m_process->setcpuaffinity(this->m_affinity);
            m_process->wait(maxProcess, std::bind(&TcpServer::run, this));   
        }
        else
        {
            run();
        }
    } 

    void TcpServer::start(const Address& addr, const ConnEventCallback_t& callback,size_t maxProcess)
    {
        m_reuseport = ( is_support_reuseport() && m_reuseport ) ? true : false;
    	m_connEventCallback = callback;
		m_port = addr.port();

		if(!m_reuseport || m_isunsock)
		{
    		if(listen(addr,callback) < 0 )
            {
               return;
            }
		}
    	start(maxProcess);
    }    

    #if 0
    void TcpServer::start(const string& conf,const ConnEventCallback_t& callback)
    {
        ServConf sconf;
        if(sconf.loadConf(conf) != 0)
        {
            LOG_ERROR("loadConf %s error:%s",strerror(errno));
            return;
        }
        uint16_t maxProcess = sconf.cfgGetMaxprocess();
        worker_connections = sconf.cfgGetWorkerConnections();

        setMaxIdleTimeout(sconf.cfgGetMaxIdleTimeout());
        setReuseport(sconf.cfgIsReuseport());
        if(sconf.cfgIsUnsock()) setUnsock();
        setUnpath(sconf.cfgGetUnpath());

        Address addr(sconf.cfgGetIPAddr(),sconf.cfgGetPort());
        return start(addr,callback,maxProcess);
    }
    #endif

    void TcpServer::run()
    {
    	if(m_reuseport && !m_isunsock)
		{
			Address addr(m_port);
			if(listen(addr,m_connEventCallback) < 0 )
			{
				exit(-1);
			}	
    	}
        if(m_running)
        {
            return;    
        }
        LOG_INFO("epoll event triggered mode:%s",m_et?"Edge Triggered":"Level Triggered");
        
        m_loop = new IOLoop(m_et);
        m_running = true;
        m_loop->addCallback(std::bind(&TcpServer::onRun, this));
        m_loop->start();
    }

    void TcpServer::onRun()
    {
        LOG_INFO("tcp server on run %d",getpid());
        for_each(m_acceptors.begin(), m_acceptors.end(),  std::bind(&Acceptor::start, _1, m_loop));
        vector<int> signums{SIGINT, SIGTERM,SIG_IOWEVENT};
        m_signaler = std::make_shared<Signaler>(signums, std::bind(&TcpServer::onSignal, this, _1, _2)); 
        m_signaler->start(m_loop);
        m_idleWheel = std::make_shared<TimingWheel>(1000, 3600);
        m_idleWheel->start(m_loop);
        m_runCallback(m_loop);
    }

    void TcpServer::onStop()
    {
        LOG_INFO("tcp server on stop %d",getpid());
        if(!m_running)
        {
            return;    
        }
        m_running = false;
        m_idleWheel->stop();
        m_signaler->stop();
        for_each_all(m_acceptors, std::bind(&Acceptor::stop, _1));
        m_loop->stop();    
    }

    void TcpServer::stop()
    {
        LOG_INFO("stop server");
        m_process->stop(); 
        onStop();
    }

    void TcpServer::onNewConnection(IOLoop* loop, int fd, const ConnEventCallback_t& callback)
    {
        ConnectionPtr_t conn = std::make_shared<Connection>(loop, fd);
        conn->setEventCallback(callback);
        conn->onEstablished();
        int afterCheck = m_maxIdleTimeout / 2 + random() % m_maxIdleTimeout;
        m_idleWheel->add(std::bind(&TcpServer::onIdleConnCheck, this, _1, WeakConnectionPtr_t(conn)), afterCheck * 1000);
        return;
    }

    void TcpServer::onIdleConnCheck(const TimingWheelPtr_t& wheel, const WeakConnectionPtr_t& conn)
    {
        ConnectionPtr_t c = conn.lock();
        if(!c)
        {
            return;
        }        
    
        struct timespec t;
        clock_gettime(CLOCK_MONOTONIC, &t); 
        uint64_t now = t.tv_sec;
        if(now - c->lastActiveTime() > (uint64_t)m_maxIdleTimeout)
        {
            LOG_INFO("%d timeout, force shutdown",c->getSockFd() );
            c->shutDown();
        }
        else
        { 
            //check interval is (maxIdleTimeout * 9 / 10) * 1000
            m_idleWheel->add(std::bind(&TcpServer::onIdleConnCheck, this, _1, WeakConnectionPtr_t(c)), m_maxIdleTimeout * 900);
        }
    }

    void TcpServer::onSignal(const SignalerPtr_t& signaler, int signum)
    {
        int sigval = 0;
        switch(signum)
        {
            case SIGINT:
            case SIGTERM:
                {
                	LOG_INFO("recv signal %d,start execl stop process", signum);
                    //onStop();
                	stop();
                }
                break;
            case SIG_IOWEVENT: // add by wanghm 2020
                sigval = signaler->getsigval();
                if(sigval > 0)
                {
                    m_loop->updateHandler(sigval, TNET_READ | TNET_WRITE);
                    LOG_TRACE("recv SIG_IOWEVENT signal %d,the sockfd %d write event available", signum,sigval);
                }
                break;   
            default:
                LOG_ERROR("invalid signal %d", signum);
                break;
        }    
    }  
}
